using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.Logic;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class UIStory : UIEntityWindow
    {
        #region Template Generate,don't modify

        protected class UIB_UIStory
        {
            #region ObjectBinding Generate

            public UnityEngine.GameObject textPanel { protected set; get; }
            public IQIGame.Onigao.Framework.ExText tex_SpeakName { protected set; get; }
            public IQIGame.Onigao.GamePlay.ExButtonStory btn_bg { protected set; get; }
            public UnityEngine.GameObject optionItem { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btn_Skip { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btn_Review { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btn_AutoPlay { protected set; get; }
            public IQIGame.Onigao.Framework.ExRawImage videoPanel { protected set; get; }
            public IQIGame.Onigao.GamePlay.TypewriterText tex_Content { protected set; get; }
            public UnityEngine.GameObject uIN_SpeakerItem { protected set; get; }
            public IQIGame.Onigao.Framework.ExCanvas bGPanel { protected set; get; }
            public IQIGame.Onigao.Framework.ExCanvas speakerPanel { protected set; get; }
            public IQIGame.Onigao.Framework.ExCanvas frontPanel { protected set; get; }
            public UnityEngine.RectTransform effectPanel { protected set; get; }
            public UnityEngine.GameObject centerPanel { protected set; get; }
            public IQIGame.Onigao.GamePlay.TypewriterText Tex_CenterContent { protected set; get; }
            public UnityEngine.GameObject[] SpeakerPoint { protected set; get; }
            public UnityEngine.GameObject uIN_StoryBackground { protected set; get; }
            public IQIGame.Onigao.Framework.ExButton btn_AutoSpeed { protected set; get; }
            public IQIGame.Onigao.Framework.ExText speed_Text { protected set; get; }

            public virtual void InitBinding(ObjectBinding __binding)
            {
                __binding.TryGetVariableValue<UnityEngine.GameObject>("textPanel", out var __tbv0);
                this.textPanel = __tbv0;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("tex_SpeakName", out var __tbv1);
                this.tex_SpeakName = __tbv1;
                __binding.TryGetVariableValue<IQIGame.Onigao.GamePlay.ExButtonStory>("btn_bg", out var __tbv2);
                this.btn_bg = __tbv2;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("optionItem", out var __tbv3);
                this.optionItem = __tbv3;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btn_Skip", out var __tbv4);
                this.btn_Skip = __tbv4;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btn_Review", out var __tbv5);
                this.btn_Review = __tbv5;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btn_AutoPlay", out var __tbv6);
                this.btn_AutoPlay = __tbv6;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExRawImage>("videoPanel", out var __tbv7);
                this.videoPanel = __tbv7;
                __binding.TryGetVariableValue<IQIGame.Onigao.GamePlay.TypewriterText>("tex_Content", out var __tbv8);
                this.tex_Content = __tbv8;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("uIN_SpeakerItem", out var __tbv9);
                this.uIN_SpeakerItem = __tbv9;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExCanvas>("bGPanel", out var __tbv10);
                this.bGPanel = __tbv10;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExCanvas>("speakerPanel", out var __tbv11);
                this.speakerPanel = __tbv11;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExCanvas>("frontPanel", out var __tbv12);
                this.frontPanel = __tbv12;
                __binding.TryGetVariableValue<UnityEngine.RectTransform>("effectPanel", out var __tbv13);
                this.effectPanel = __tbv13;
                __binding.TryGetVariableValue<UnityEngine.GameObject>("centerPanel", out var __tbv14);
                this.centerPanel = __tbv14;
                __binding.TryGetVariableValue<IQIGame.Onigao.GamePlay.TypewriterText>("Tex_CenterContent", out var __tbv15);
                this.Tex_CenterContent = __tbv15;
                var __tbv16 = __binding.GetVariableByName("SpeakerPoint");
                this.SpeakerPoint = new UnityEngine.GameObject[__tbv16?.arrayValueCount ?? 0];
                for (int __index = 0; __index < this.SpeakerPoint.Length; __index++)
                {
                    this.SpeakerPoint[__index] = __tbv16.variableArray.GetValue<UnityEngine.GameObject>(__index);
                }
                __binding.TryGetVariableValue<UnityEngine.GameObject>("uIN_StoryBackground", out var __tbv17);
                this.uIN_StoryBackground = __tbv17;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExButton>("btn_AutoSpeed", out var __tbv18);
                this.btn_AutoSpeed = __tbv18;
                __binding.TryGetVariableValue<IQIGame.Onigao.Framework.ExText>("speed_Text", out var __tbv19);
                this.speed_Text = __tbv19;
            }

            #endregion ObjectBinding Generate
        }

        #endregion Template Generate,don't modify

        #region Window Config

        /// <summary>
        /// 静态配置
        /// </summary>
        private static UICreateConfig _createConfig = new()
        {
            prefabName = nameof(UIStory),
            layer = EUILayer.Normal,
            isFullScreen = true
        };

        /// <summary>
        /// 创建UI的配置
        /// </summary>
        public override IUICreateConfig createConfig => _createConfig;

        #endregion Window Config

        #region fields

        private StoryNodeConfig _curNode;

        // 选项item池子
        private UIItemPool<UIN_StoryOptionItem> _poolOptionItems;
        private Action<int> _chooseOptionEvent;
        private UIItemPool<UIN_SpeakerItem> _poolSpeaker;
        private UIItemPool<UIN_StoryBackground> _poolBackGround;

        private List<StoryChooseConfig> _choose;
        private bool _auto;

        private UIN_StoryBackground _curBackGround;

        // (NpcID, Item)
        private readonly (int, UIN_SpeakerItem)[] _speakerItems = new (int, UIN_SpeakerItem)[3];

        // 展示当前剧情的缓存list
        private readonly List<UniTask> _showTask = new List<UniTask>();
        // 说话人action的缓存list
        private readonly List<UniTask> _speakerActionTask = new List<UniTask>();

        private readonly List<StoryAction> _actions = new();
        private readonly Dictionary<GameObject, StoryAction> _actionsMap = new();

#if UNITY_EDITOR
        public GameObject optionItemPrefab => this.ui.optionItem;
        public GameObject speakerItemPrefab => this.ui.uIN_SpeakerItem;
#endif

        #endregion fields

        #region properties

        protected UIB_UIStory ui { get; set; }
        protected UIMsgDispatcher uiMsgDispatcher => this._msgDispatcher as UIMsgDispatcher;

        #endregion properties

        protected override void BeforeInit()
        {
            ui = new UIB_UIStory();
            ui.InitBinding(this.csObjBind);
        }

        protected override void OnInit()
        {
            this._poolOptionItems = this.InitPool<UIN_StoryOptionItem>(this.ui.optionItem);
            this._poolSpeaker = this.InitPool<UIN_SpeakerItem>(this.ui.uIN_SpeakerItem);
            this._poolBackGround = this.InitPool<UIN_StoryBackground>(this.ui.uIN_StoryBackground);
            this._chooseOptionEvent = this.OnChoseOptionItemClicked;
            this.ui.btn_bg.onClick.AddListener(this.Next);
            this.ui.btn_Skip.onClick.AddListener(this.Skip);
            this.ui.btn_AutoPlay.onClick.AddListener(this.Auto);
            this.ui.btn_AutoSpeed.onClick.AddListener(this.SetSpeed);

            this.isUpdate = true;
        }

        protected override void OnShow(UIBaseData data)
        {
            this.ui.bGPanel.UpdateSortingOrderByParent();
            this.ui.speakerPanel.UpdateSortingOrderByParent();
            this.ui.frontPanel.UpdateSortingOrderByParent();
        }

        protected override void OnHide()
        {
            for (int i = 0; i < this._actions.Count; i++)
            {
                this._actions[i].Dispose();
            }
            this._actions.Clear();
            this._actionsMap.Clear();
        }

        /// <summary>
        /// 添加UGUI侦听
        /// </summary>
        protected override void AddUGUIListener()
        {
        }

        /// <summary>
        /// 添加全局侦听（MsgDispatcher）
        /// </summary>
        protected override void AddMsgListeners()
        {
        }

        protected override void OnDispose()
        {
            this.Clear();
        }

        protected override void OnUpdate(float dt, float unscaledDt)
        {
            base.OnUpdate(dt, unscaledDt);
            this.ui.tex_Content.OnUpdate(dt);
            this.ui.Tex_CenterContent.OnUpdate(dt);

            // 倒序循环来处理update期间被移除
            var rList = this._poolSpeaker.listItems;
            var nLen = this._poolSpeaker.listItems.Count;
            for (int i = 0; i < nLen; i++)
            {
                var nIndex = nLen - i - 1;
                rList[nIndex].Update(dt, unscaledDt);
            }

            for (int i = 0; i < this._actions.Count; i++)
            {
                this._actions[i].Update(dt);
            }
        }

        // 默认为true，用来在首次开启的时候关闭其他全屏UI
        private bool _fullScreen = true;

        protected override bool IsFullScreen()
        {
            return this._fullScreen;
        }

        #region methonds

        private void Next()
        {
            if (this.ui.tex_Content.isPlaying)
            {
                this.ui.tex_Content.Skip();
            }
            else
            {
                StoryManager.Instance.Next();
            }
        }

        private void Jump(int id)
        {
            StoryManager.Instance.Jump(id);
        }

        private void Skip()
        {
            var rMsg = StoryManager.Instance.context.config.synopsis;
            if (string.IsNullOrEmpty(rMsg))
            {
                StoryManager.Instance.Skip();
            }
            else
            {
                NoticeModule.ShowNotice(NoticeConstant.ID_CommonConfirmBtn2, StoryManager.Instance.Skip, null, null, rMsg);
            }
        }

        private void Auto()
        {
            this._auto = StoryManager.Instance.Auto(!this._auto);
        }

        private void SetSpeed()
        {
            if (this._auto)
            {
                var nOldSpeed = StoryManager.Instance.speedMultiplier;
                var nSpeed = 2 * nOldSpeed;
                if (nSpeed > 4)
                {
                    nSpeed = 1;
                }
                this.ui.btn_AutoSpeed.text = "x " + StoryManager.Instance.SetSpeed(nSpeed);
            }
        }

        public void ChooseOption(int index)
        {
            var rChoose = this._choose[index];
            StoryManager.Instance.OnOptionChoose(index);
            if (rChoose.jumpID > 0)
            {
                this.Jump(rChoose.jumpID);
            }
            else
            {
                this.End();
            }
        }

        public void Clear()
        {
            for (int i = 0; i < this._speakerItems.Length; i++)
            {
                this._speakerItems[i] = default;
            }
            this._poolOptionItems.HideAll();
            this._poolSpeaker.HideAll();
            this._poolBackGround.HideAll();
            this._curBackGround = null;
        }

        public UniTask ShowStory(StoryNodeConfig node)
        {
            // 清理旧表现
            this.ClearLastDisplay();
            this._showTask.Clear();

            this._curNode = node;
            foreach (var rPair in node.customConfig)
            {
                switch (rPair.Value)
                {
                    case StoryNode_Normal rNormal:
                        if (!string.IsNullOrEmpty(rNormal.contents))
                        {
                            this.ui.textPanel.gameObject.SetActive(true);
                            var rSpeakerName = rNormal.speaker;
                            if (rNormal.speakerID > 0)
                            {
                                var rNpcConfig = TableCenter.storyNpc.Get(rNormal.speakerID);
                                if (rNpcConfig != null)
                                {
                                    rSpeakerName = rNpcConfig.Name;
                                }
                            }
                            this.ui.tex_SpeakName.text = rSpeakerName;
                            this._showTask.Add(this.SetContents(rNormal.contents));
                        }
                        this.ShowBackground((EStoryBackGroundType)rNormal.backgroundType, rNormal.background).Forget();
                        break;
                    case StoryNode_Option rOption:
                        var rOptionList = rOption.choose;
                        this.ui.tex_Content.RegisterOnContentComplete(() => this.ShowChooseOption(rOptionList));
                        break;
                    case StoryNode_Effect rEffect:
                        // TODO 等待系统UI特效播放方案确认
                        break;
                    case StoryNode_CenterContents rCenter:
                        this.ui.centerPanel.gameObject.SetActive(true);
                        this.ui.Tex_CenterContent.targetText = rCenter.contents;
                        break;
                    case StoryNode_Speaker rSpeaker:
                        this._showTask.Add(this.SetSpeaker(rSpeaker));
                        break;
                    // bgAction必须在bg之后
                    case StoryNode_BackgroundAction rBgAction:
                        {
                            if (this._curBackGround != null)
                            {
                                var rActionConfig = StoryActionGlobal.Get(rBgAction.action);
                                var rAction = StoryFactoryClient.GetAction(this, this._curBackGround.gameObject, rActionConfig.data);
                                rAction.BindDynamic(EStoryActionDynamicKey.AlphaGetter, (Func<float>)this._curBackGround.GetAlpha);
                                rAction.BindDynamic(EStoryActionDynamicKey.AlphaSetter, (Action<float>)this._curBackGround.SetAlpha);
                                this._showTask.Add(this.AddAction(rAction));
                                this._curBackGround.autoDelete = rBgAction.deleteAfterAction;
                            }
                            break;
                        }
                }
            }
            return UniTask.WhenAll(this._showTask);
        }

        private void ClearLastDisplay()
        {
            this.ui.textPanel.gameObject.SetActive(false);
            this.ui.centerPanel.gameObject.SetActive(false);
            this._poolOptionItems.HideAll();
            this.ui.tex_Content.UnRegisterOnContentComplete();

            if (this._curBackGround != null)
            {
                var rBackGround = this._curBackGround;
                if (rBackGround.autoDelete)
                {
                    this._curBackGround = null;
                    this.RemoveActionWithHide(rBackGround).Forget();
                }
                else
                {
                    this.RemoveAction(rBackGround.gameObject).Forget();
                }
            }
        }

        private async UniTask RemoveActionWithHide(UIN_StoryBackground rBackGround)
        {
            await this.RemoveAction(rBackGround.gameObject);
            this._poolBackGround.HideOne(rBackGround);
        }

        private async UniTask SetSpeaker(StoryNode_Speaker speaker)
        {
            var rList = speaker.speaker;
            // 特意分两次循环来减少如 3号位角色改到1号位  这种情况下导致的池子冗余
            for (int i = 0; i < this._speakerItems.Length; i++)
            {
                (int nId, var rItem) = this._speakerItems[i];
                if (nId > 0 && (i >= rList.Count || rList[i].npc != nId))
                {
                    this.RemoveSpeaker(rItem).Forget();
                    this._speakerItems[i] = default;
                }
            }
            this._speakerActionTask.Clear();
            for (int i = 0; i < rList.Count; i++)
            {
                var rConfig = rList[i];
                (int nId, var rItem) = this._speakerItems[i];
                if (rConfig.npc != 0)
                {
                    if (nId != rConfig.npc)
                    {
                        nId = rConfig.npc;
                        rItem = this._poolSpeaker.GetOne();
                        this._speakerItems[i] = (nId, rItem);
                        await rItem.Refresh(rConfig.speakerType, rConfig.npc, this.ui.speakerPanel);
                    }
                    if (rConfig.active)
                    {
                        rItem.Active();
                    }
                    else
                    {
                        rItem.DeActive();
                    }
                    rItem.gameObject.transform.localPosition = this.GetSpeakerPoint(i).transform.localPosition;
                    if (rConfig.action != 0)
                    {
                        var rActionConfig = StoryActionGlobal.Get(rConfig.action);
                        if (rActionConfig != null)
                        {
                            var rAction = StoryFactoryClient.GetAction(this, rItem.gameObject, rActionConfig.data);
                            rAction.BindDynamic(EStoryActionDynamicKey.SelfPoint, i);
                            rAction.BindDynamic(EStoryActionDynamicKey.AlphaGetter, (Func<float>)rItem.GetAlpha);
                            rAction.BindDynamic(EStoryActionDynamicKey.AlphaSetter, (Action<float>)rItem.SetAlpha);
                            rAction.BindDynamic(EStoryActionDynamicKey.Animation, (Action<string, bool>)rItem.PlayAnimation);
                            this._speakerActionTask.Add(this.AddAction(rAction));
                        }
                    }
                }
            }
            await UniTask.WhenAll(this._speakerActionTask);
        }

        private async UniTaskVoid RemoveSpeaker(UIN_SpeakerItem item)
        {
            await this.RemoveAction(item.gameObject);
            this._poolSpeaker.HideOne(item);
        }

        private async UniTask AddAction(StoryAction action)
        {
            if (action == null)
            {
                return;
            }
            this._actions.Add(action);
            if (this._actionsMap.TryGetValue(action.root, out var rOldAction))
            {
                rOldAction.Dispose();
            }
            this._actionsMap[action.root] = action;
            action.BeforeStart();
            await action.Start();
        }

        private async UniTask RemoveAction(GameObject root)
        {
            if (this._actionsMap.TryGetValue(root, out var rAction))
            {
                this._actionsMap.Remove(rAction.root);
                this._actions.Remove(rAction);
                rAction.BeforeEnd();
                await rAction.End();
                rAction.Dispose();
            }
        }

        private void End()
        {
#if UNITY_EDITOR
            if (StoryClientUtil.IsEditorMode)
            {
                return;
            }
            else
#endif
            {
                this.Dispose();
            }
        }

        private void OnChoseOptionItemClicked(int index)
        {
            this.ChooseOption(index);
        }

        private UniTask SetContents(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                this.ui.tex_Content.targetText = "";
            }
            else
            {
                if (GameDataCenter.PlayerData != null)
                {
                    value = value.Replace("<playername>", GameDataCenter.PlayerData.playerName);
                }
                this.ui.tex_Content.targetText = value;
            }
            return this.ui.tex_Content.ToUniTask();
        }

        private async UniTaskVoid ShowBackground(EStoryBackGroundType backGroundType, string imagePath)
        {
            if (this._curBackGround != null)
            {
                if (this._curBackGround.Equals(backGroundType, imagePath))
                {
                    return;
                }
                this._poolBackGround.HideOne(this._curBackGround);
                this._curBackGround = null;
                this._fullScreen = false;
            }
            if (backGroundType != EStoryBackGroundType.Delete)
            {
                this._curBackGround = this._poolBackGround.GetOne();
                this._fullScreen = await this._curBackGround.ShowBackground(backGroundType, imagePath);
            }
        }

        /// <summary>
        /// 选项
        /// </summary>
        private void ShowChooseOption(List<StoryChooseConfig> chooses)
        {
            this._choose = chooses;
            if (chooses == null || chooses.Count == 0)
            {
                return;
            }
            for (int i = 0; i < chooses.Count; i++)
            {
                var rChoose = chooses[i];
                int conditionId = rChoose.conditionShow;
                if (conditionId > 0 && !StoryManager.Instance.context.CheckCondition(conditionId))
                {
                    continue;
                }
                bool bCanSelect = true;
                if (conditionId > 0)
                {
                    conditionId = chooses[i].conditionSelect;
                    bCanSelect = StoryManager.Instance.context.CheckCondition(conditionId);
                }
                var optionContent = rChoose.contents ?? "";
                var item = this._poolOptionItems.GetOne();
                item.InitOptionItem(i, optionContent, bCanSelect ? this._chooseOptionEvent : null);
            }
        }

        public GameObject GetSpeakerPoint(int point)
        {
            if (point >= 0 && point < this.ui.SpeakerPoint.Length)
            {
                return this.ui.SpeakerPoint[point];
            }
            return null;
        }

        #endregion methonds
    }
}